# -*- coding: utf-8 -*-
"""
    根据交易记录计算策略执行后的各种指标，并返回计算结果。
    净值（NV）计算方法：假设起始 t=0 时为 NV_0＝1，取对数后记 NV_0＝0，持仓 position_0=0
    1. 如果 t 周期，position_t=0 , 则 NV_t=NV_t-1,如果此时，
        a. openLongCond 为 True, 则记 position_t=1 ,trade_price = close_t+slip_points,NV_t=NV_t-1+ln(close_t)-ln(trade_price) 此周期相关计算结束；
        b. openShortCond 为 True, 则记 position_t=-1 ,trade_price = close_t-slip_points, NV_t=NV_t-1-ln(close_t)+ln(trade_price)此周期相关计算结束；
        c.  此周期相关计算结束；
    2.  如果 t 周期，position_t=1,
        a. closeLongCond 为 True, 则记 position_t=0, trade_price = close_t-slip_points, NV_t=NV_t-1+ln(trade_price)-ln(close_t-1);
           若 openShortCond  为 True , 则记 position_t=-1, trade_price = close_t-slip_points,  NV_t=NV_t-ln(close_t)+ln(trade_price)，结束此周期计算；
        b. NV_t=NV_t-1+ln(close_t)-ln(close_t-1),结束此周期计算；
    3.  如果 t 周期，position_t=-1,
        a. closeShortCond 为 True, 则记 position_t=0, trade_price = close_t+slip_points, NV_t=NV_t-1-ln(trade_price)+ln(close_t-1);
           若 openLongCond  为 True , 则记 position_t=1, trade_price = close_t+slip_points,  NV_t=NV_t+ln(close_t)-ln(trade_price)，结束此周期计算；
        b. NV_t=NV_t-1-ln(close_t)+ln(close_t-1),结束此周期计算；
"""

import numpy as np
import pandas as pd

from utils.boxi_dict import BoxiDict

# 利润率计算方式：单利=1，复利=2
RATE_METHOD_SIMPLE = 1
RATE_METHOD_COMPOUND = 2
# 利润（率）计算方向
PROFIT_DIRECTION_BOTH = 0
PROFIT_DIRECTION_LONG = 1
PROFIT_DIRECTION_SHORT = 2
# 交易记录中 sgn_td_price 列所代表的意义
PRICE_TYPE_MONEY = 'money'  # 交易用价格
PRICE_TYPE_LOG = 'LOG'  # 价格的自然对数

# 持仓方向
POSITION_DIRECTION_NONE = 0  # 无持仓
POSITION_DIRECTION_LONG = 1  # 持多仓
POSITION_DIRECTION_SHORT = -1  # 持空仓
POSITION_DIRECTION_BOTH = 2  # 双向持仓


class TradeResultData:
    """回测结果数据,不区分多头交易和空头交易"""
    oc_flag_srs = None  # 开平标志
    td_srs = None  # 开平交易序列
    win_srs = None  # 盈利交易序列
    loss_srs = None  # 亏损交易序列
    td_prft_rate = None  # 总利润率
    td_nv = None  # 成交后净值序列
    td_max_nv_srs = None  # 成交后最大净值序列
    td_max_nv = None  # 成交后最大净值
    td_max_drb_srs = None  # 最大回撤序列
    td_max_drb = None  # 最大回撤

    win_prft_rate = None  # 盈利交易每次收益率
    win_prft = None  # 总盈利
    max_win_prft = None  # 最大单次盈利
    loss_prft_rate = None  # 亏损交易每次收益率
    loss_prft = None  # 总亏损
    max_loss_prft = None  # 最大单次亏损
    td_prft = None  # 总利润
    td_count = None  # 总交易次数
    win_count = None  # 盈利交易次数
    loss_count = None  # 亏损交易次数
    win_ratio = None  # 胜率
    win_loss_ratio = None  # 盈亏比
    avg_td_prft = None  # 平均每笔利润
    avg_win_prft = None  # 盈利平均每笔利润
    avg_loss_prft = None  # 亏损平均每笔利润
    # 持仓相关
    pstn_nv = None  # 持仓净值序列
    pstn_max_nv_srs = None  # 持仓最大净值序列
    pstn_max_nv = None  # 持仓最大净值
    pstn_max_drb_srs = None  # 持仓最大回撤序列
    pstn_max_drb = None  # 持仓最大回撤
    pstn_prd = None  # 持仓周期
    pstn_max_float_win_srs = None  # 持仓最大浮盈序列（多仓按最高价计，空仓按最低价计）
    pstn_max_float_loss_srs = None  # 持仓最大浮亏序列（多仓按最低价计，空仓按最高价计）
    avg_pstn_prd = None  # 每笔交易平均持仓周期
    per_pstn_prd_srs = None  # 每笔持仓周期序列

    total_prd = None  # 总回测周期
    per_pstn_prd = None  # 每笔持仓周期
    per_win_prd = None  # 盈利每笔持仓周期
    per_loss_prd = None  # 亏损每笔持仓周期
    avg_win_prd = None  # 盈利交易持仓平均周期
    avg_loss_prd = None  # 亏损交易持仓平均周期
    max_win_prd = None  # 盈利交易持仓最长周期
    max_loss_prd = None  # 亏损交易持仓最长周期

    # 效率相关（与比较基准有关）
    open_tm_eff = None  # 开仓时间效率
    close_tm_eff = None  # 平仓时间效率
    open_price_eff = None  # 开仓价格效率
    close_price_eff = None  # 平仓价格效率
    open_eff = None  # 开仓效率
    close_eff = None  # 平仓效率
    td_eff = None  # 交易效率
    stra_eff = None  # 策略效率

    scale_attr = [td_count, win_count, loss_count, win_ratio,
                  td_prft, win_prft, loss_prft, win_loss_ratio, max_win_prft, max_loss_prft,
                  total_prd, avg_pstn_prd, avg_win_prd, avg_loss_prd, max_win_prd, max_loss_prd, pstn_max_drb]


def join(lr: TradeResultData, sr: TradeResultData, price_type=PRICE_TYPE_LOG):
    """
    将多头结果与空头结果合并，生成多空同时交易的结果 tr
    :param lr: 多头结果
    :param sr: 空头结果
    :param price_type: PRICE_TYPE_LOG＝取对数后的价格，PRICE_TYPE_MONEY＝原始的以货币计价
    :return: TradeResultData
    """
    tr = TradeResultData()
    tr.td_srs = lr.td_srs.append(sr.td_srs).sort()
    tr.td_prft_rate = lr.td_prft_rate.append(sr.td_prft_rate).sort()

    tr.td_nv = (tr.td_prft_rate + 1).cumprod()

    tr.td_count = lr.td_count + sr.td_count
    tr.win_count = lr.win_count + sr.win_count
    tr.loss_count = lr.loss_count + sr.loss_count
    tr.td_prft = lr.td_prft

    return tr


def clc_long_td_rslt(data, base_data=None, sgn_td_price='close', sgn_close_price='close',
                     sgn_datetime='datetime',
                     sgn_open='buy', sgn_close='sell',
                     open_cond='buy>=1', close_cond='sell>=1',
                     sgn_vol='vol', multiplier=1, margin_rate=100, fee_rate=0.0, slip_points=0,
                     instrument_info=None):
    """
    计算做多时：回测结果
    :param data: 交易序列，类型：pd.DataFrame,包含日期时间（datetime)、收盘价格（close）、
    交易价格(price)、开多(buy)、平多(sell)、开空(short)、平空(cover)、交易量(vol)
    应是 Bars or Ticks from indicator.df_bar.py or df_tick.py
    :param base_data:基准数据, 包含开 平仓时间及价格: datetime,bssc,price
                    bssc:1,2:开多或平空,-1,-2:平多或开空; 4:平空及开多, -4: 平多及开空
    :param sgn_td_price: 交易用价格列名
    :param sgn_close_price: 用于计算持仓时资产的价格,收盘价
    :param sgn_datetime:索引字段名称
    :param sgn_open: 开多手数列名，>0:开多手数
    :param sgn_close: 平多手数列名，>0: 平多手数
    :param open_cond:开多条件
    :param close_cond:平多条件
    :param sgn_vol: 交易量
    :param multiplier: 乘数
    :param slip_points: 滑点
    :param margin_rate: 保证金率（%）
    :param fee_rate: 手续费率，>1:每笔手续费，<1:按金额计算的手续费率
    :param instrument_info: 交易标的信息，，至少包括：multiplier(乘数),fee(fee_rate)（费用或费率）,margin_rate（保证金率%）
    对于股票来说：instrument_info={'multiplier':1,'fee_rate':0.5,'margin_rate':100}
    期货假定：instrument_info={'multiplier':10,'fee_rate':0.01,'margin_rate':20}
    :return: TradeResultData

    使用方法：
        对于基于 Ticks 的策略,
      btr = clc_long_td_rslt(df, sgn_datetime='DateTime',
                               sgn_td_price='LastPrice', sgn_close_price='LastPrice'
                               sgn_open='valleyIs', sgn_close='peakIs',
                               open_cond='bssc>0', close_cond='bssc<0',
                               sgn_vol='Volume', multiplier=1, slip_points=0)
        对于基于 Bars 的策略,
      btr = calc_long_td_result(df, sgn_datetime='datetime',
                               sgn_td_price='close', sgn_close_price='close'
                               sgn_open='valleyIs', sgn_close='peakIs',
                               open_cond='bssc>0', close_cond='bssc<0',
                               sgn_vol='Volume', multiplier=1, slip_points=0)
    """
    data.drop_duplicates(keep='last', inplace=True)
    data_len = len(data.index)
    # 价格取对数后便于计算收益率
    o_p = (data[sgn_td_price] + slip_points) * (1 + fee_rate)  # 开仓价格
    c_p = (data[sgn_td_price] - slip_points) * (1 - fee_rate)  # 平仓价格
    logc = np.log(data[sgn_close_price])  # 收盘价对数
    logb = np.log(o_p)  # 开仓价对数
    logs = np.log(c_p)  # 平仓价对数
    data['logc'] = logc
    data['logb'] = logb
    data['logs'] = logs

    oc = pd.Series(data=[0] * data_len, index=data.index)  # 预置开仓、平仓标志：0
    # print('oc[data.query(open_cond).index]:\n',data.query(open_cond).index)
    oc[data.query(open_cond).index] = 1  # 开仓时置开平标志：1
    oc[data.query(close_cond).index] = -1  # 平仓时置开平标志：-1
    data['oc'] = oc
    td_srs = data.query('oc==1 or oc==-1')
    if td_srs.empty:
        print('there is no trade signals in the series..')
        return

    # 将开仓价格与平仓价格串起来，开仓时：logp = logb,平仓时：logp = logs
    logp = pd.concat([data.query('oc==1').logb, data.query('oc==-1').logs])
    logp.sort_index(inplace=True)  # 按索引重排序
    data['logp'] = logp
    # 处理第一个信号不是开仓信号、连续同向信号、最后一个是开仓信号的情况
    oc_sum = td_srs['oc'].rolling(window=2, min_periods=1).sum()
    if oc_sum[0] == 1:  # 保留第一个开仓信号
        oc_sum[0] = 0
    else:
        oc[oc_sum.index[0]] = 0  # 删除第一个非开仓信号
    if oc[oc_sum.index[-1]] == 1:  # 删除最后一个信号是开仓信号的
        oc_sum[-1] = 1
    data['oc'] = oc
    td_srs = data.loc[oc_sum[oc_sum == 0].index, :]
    if len(td_srs) == 0:
        return {}
    o_srs = td_srs.query('oc==1')  # 开仓序列
    c_srs = td_srs.query('oc==-1')  # 平仓序列

    # 每笔交易(平仓)利润率
    td_prft_rate = pd.Series(np.exp(td_srs['logp'].diff(1)[1::2]) - 1, name='td_prft_rate')
    # 平仓后净值序列
    td_nv = (td_prft_rate + 1).cumprod()
    # 最大净值序列
    td_max_nv_srs = td_nv.rolling(window=len(td_prft_rate), min_periods=1).max()
    # 最大回撤序列
    td_max_drb_srs = (td_max_nv_srs - td_nv) / td_max_nv_srs * 100
    td_max_nv = td_max_nv_srs.max()
    td_max_drb = td_max_drb_srs.max()

    win_prft_rate = td_prft_rate[td_prft_rate > 0]  # 盈利交易每次收益率
    win_prft = win_prft_rate.sum()  # 总盈利
    max_win_prft = win_prft_rate.max()  # 最大单次盈利
    loss_prft_rate = td_prft_rate[td_prft_rate < 0]  # 亏损交易每次收益率
    loss_prft = loss_prft_rate.sum()  # 总亏损
    max_loss_prft = loss_prft_rate.min()  # 最大单次亏损
    td_prft = win_prft + loss_prft  # 总交易利润
    td_count = len(td_prft_rate)  # 总交易次数
    win_count = len(win_prft_rate)  # 盈利交易次数
    loss_count = len(loss_prft_rate)  # 亏损交易次数
    win_ratio = win_count / td_count  # 胜率
    win_loss_ratio = 100 if loss_prft == 0 else win_prft / loss_prft  # 盈亏比
    avg_td_prft = td_prft / td_count  # 平均每笔利润
    avg_win_prft = 0 if win_count == 0 else win_prft / win_count  # 盈利平均每笔利润
    avg_loss_prft = 0 if loss_count == 0 else loss_prft / loss_count  # 亏损平均每笔利润

    # 持仓分析
    # 持仓标志：1==多，-1==空
    l_holding_flag = oc.replace(to_replace=0, method='ffill')
    data['l_holding_flag'] = l_holding_flag
    # 持仓时间序列（含平仓）
    pstn_srs = data.query('l_holding_flag==1 or oc==-1')
    # 持仓期间每期收益率
    pstn_prft_rate = np.exp(pstn_srs['logc'].diff(1)) - 1
    pstn_prft_rate[o_srs.index] = np.exp(o_srs['logc'] - o_srs['logp']) - 1  # 开仓当期收益率
    pstn_prft_rate[c_srs.index] = np.exp(
        pstn_prft_rate[c_srs.index] + c_srs['logp'] - c_srs['logc']) - 1  # 平仓当期收益率
    pstn_nv = (pstn_prft_rate + 1).cumprod()  # 持仓净值
    pstn_max_nv_srs = pstn_nv.rolling(window=len(pstn_prft_rate), min_periods=1).max()  # 最大净值序列
    pstn_max_drb_srs = (pstn_max_nv_srs - pstn_nv) / pstn_max_nv_srs * 100  # 最大回撤序列
    pstn_max_nv = pstn_max_nv_srs.max()
    pstn_max_drb = pstn_max_drb_srs.max()

    total_prd = data_len  # 总回测周期
    pstn_prd = len(pstn_srs)  # 持仓周期
    avg_pstn_prd = pstn_prd / td_count  # 每笔交易平均持仓周期
    per_pstn_prd = pd.Series(
        {c_srs.index[i]: len(data[o_srs.index[i]:c_srs.index[i]]) for i in range(len(o_srs))},
        name='ppp')
    per_win_prd = per_pstn_prd[td_prft_rate > 0]
    per_loss_prd = per_pstn_prd[td_prft_rate < 0]
    avg_win_prd = 0 if win_count == 0 else per_win_prd.sum() / win_count  # 盈利交易持仓平均周期
    avg_loss_prd = 0 if loss_count == 0 else per_loss_prd.sum() / loss_count  # 亏损交易持仓平均周期
    max_win_prd = per_win_prd.max()  # 盈利交易持仓最长周期
    max_loss_prd = per_loss_prd.max()  # 亏损交易持仓最长周期

    tr = TradeResultData()
    tr.oc_flag_srs = oc
    tr.td_srs = td_srs
    # tr.win_srs = win_srs
    # tr.loss_srs = loss_srs
    tr.td_prft_rate = td_prft_rate
    tr.td_nv = td_nv
    tr.td_max_nv_srs = td_max_nv_srs
    tr.td_max_nv = td_max_nv
    tr.td_max_drb_srs = td_max_drb_srs
    tr.td_max_drb = td_max_drb
    tr.win_prft_rate = win_prft_rate
    tr.win_prft = win_prft
    tr.max_win_prft = max_win_prft
    tr.loss_prft_rate = loss_prft_rate
    tr.loss_prft = loss_prft
    tr.max_loss_prft = max_loss_prft
    tr.td_prft = td_prft
    tr.td_count = td_count
    tr.win_count = win_count
    tr.loss_count = loss_count
    tr.win_ratio = win_ratio
    tr.win_loss_ratio = win_loss_ratio
    tr.avg_td_prft = avg_td_prft
    tr.avg_win_prft = avg_win_prft
    tr.avg_loss_prft = avg_loss_prft
    tr.pstn_nv = pstn_nv
    tr.pstn_max_nv_srs = pstn_max_nv_srs
    tr.pstn_max_nv = pstn_max_nv
    tr.pstn_max_drb_srs = pstn_max_drb_srs
    tr.pstn_max_drb = pstn_max_drb
    tr.total_prd = total_prd
    tr.pstn_prd = pstn_prd
    tr.avg_pstn_prd = avg_pstn_prd
    tr.per_pstn_prd = per_pstn_prd
    tr.per_win_prd = per_win_prd
    tr.per_loss_prd = per_loss_prd
    tr.avg_win_prd = avg_win_prd
    tr.avg_loss_prd = avg_loss_prd
    tr.max_win_prd = max_win_prd
    tr.max_loss_prd = max_loss_prd

    # 计算效率
    if not base_data:
        return tr

    return tr


def clc_short_td_rslt(data, base_data=None, sgn_td_price='close', sgn_close_price='close',
                      sgn_datetime='datetime',
                      sgn_open='buy', sgn_close='sell',
                      open_cond='buy>=1', close_cond='sell>=1',
                      sgn_vol='vol', multiplier=1, margin_rate=100, fee_rate=0.0, slip_points=0,
                      instrument_info=None):
    """
    计算做空时：回测结果
    :param data: 交易序列，类型：pd.DataFrame,包含日期时间（datetime)、收盘价格（close）、
    交易价格(price)、开多(buy)、平多(sell)、开空(short)、平空(cover)、交易量(vol)
    :param base_data:基准数据, 包含开 平仓时间及价格: datetime,bssc,price
                    bssc:1,2:开多或平空,-1,-2:平多或开空; 4:平空及开多, -4: 平多及开空
    :param sgn_td_price: 交易用价格列名
    :param sgn_close_price: 用于计算持仓时资产的价格,收盘价
    :param sgn_datetime:索引字段名称
    :param sgn_open: 开多手数列名，>0:开多手数
    :param sgn_close: 平多手数列名，>0: 平多手数
    :param open_cond:开多条件
    :param close_cond:平多条件
    :param sgn_vol: 交易量
    :param multiplier: 乘数
    :param slip_points: 滑点
    :param margin_rate: 保证金率（%）
    :param fee_rate: 手续费率，>1:每笔手续费，<1:按金额计算的手续费率
    :param instrument_info: 交易标的信息，，至少包括：multiplier(乘数),fee(fee_rate)（费用或费率）,margin_rate（保证金率%）
    对于股票来说：instrument_info={'multiplier':1,'fee_rate':0.5,'margin_rate':100}
    期货假定：instrument_info={'multiplier':10,'fee_rate':0.01,'margin_rate':20}
    :return: TradeResultData

    使用方法：
        对于基于 Ticks 的策略,
      btr = clc_short_td_result(df, sgn_datetime='DateTime',
                               sgn_td_price='LastPrice', sgn_close_price='LastPrice'
                               sgn_open='valleyIs', sgn_close='peakIs',
                               open_cond='bssc>0', close_cond='bssc<0',
                               sgn_vol='Volume', multiplier=1, slip_points=0)
        对于基于 Bars 的策略,
      btr = calc_long_td_result(df, sgn_datetime='datetime',
                               sgn_td_price='close', sgn_close_price='close'
                               sgn_open='valleyIs', sgn_close='peakIs',
                               open_cond='bssc>0', close_cond='bssc<0',
                               sgn_vol='Volume', multiplier=1, slip_points=0)
    """
    data_len = len(data.index)
    # 价格取对数后便于计算收益率
    o_p = (data[sgn_td_price] + slip_points) * (1 + fee_rate)  # 开仓价格
    c_p = (data[sgn_td_price] - slip_points) * (1 - fee_rate)  # 平仓价格
    logc = np.log(data[sgn_close_price])  # 收盘价对数
    logb = np.log(o_p)  # 开仓价对数
    logs = np.log(c_p)  # 平仓价对数
    data['logc'] = logc
    data['logb'] = logb
    data['logs'] = logs

    oc = pd.Series(data=[0] * data_len, index=data.index)  # 预置开仓、平仓标志：0
    oc[data.query(open_cond).index] = 1  # 开仓时置开平标志：1
    oc[data.query(close_cond).index] = -1  # 平仓时置开平标志：-1
    data['oc'] = oc
    td_srs = data.query('oc==1 or oc==-1')

    # 将开仓价格与平仓价格串起来，开仓时：logp = logb,平仓时：logp = logs
    logp = pd.concat([data.query('oc==1').logb, data.query('oc==-1').logs])
    logp.sort_index(inplace=True)  # 按索引重排序
    data['logp'] = logp
    # 处理第一个信号不是开仓信号、连续同向信号、最后一个是开仓信号的情况
    oc_sum = td_srs['oc'].rolling(window=2, min_periods=1).sum()
    if oc_sum[0] == 1:  # 保留第一个开仓信号
        oc_sum[0] = 0
    else:
        oc[oc_sum.index[0]] = 0  # 删除第一个非开仓信号
    if oc[oc_sum.index[-1]] == 1:  # 删除最后一个信号是开仓信号的
        oc_sum[-1] = 1
    data['oc'] = oc
    td_srs = data.loc[oc_sum[oc_sum == 0].index, :]
    td_count = len(td_srs)  # 总交易次数
    if td_count == 0:
        return {}
    o_srs = td_srs.query('oc==1')  # 开仓序列
    c_srs = td_srs.query('oc==-1')  # 平仓序列

    # 每笔交易(平仓)利润率
    td_prft_rate = pd.Series(np.exp(0 - td_srs['logp'].diff(1)[1::2]) - 1, name='td_prft_rate')
    # 平仓后净值序列
    td_nv = (td_prft_rate + 1).cumprod()
    # 最大净值序列
    td_max_nv_srs = td_nv.rolling(window=len(td_prft_rate), min_periods=1).max()
    # 最大回撤序列
    td_max_drb_srs = (td_max_nv_srs - td_nv) / td_max_nv_srs * 100
    td_max_nv = td_max_nv_srs.max()
    td_max_drb = td_max_drb_srs.max()

    win_prft_rate = td_prft_rate[td_prft_rate > 0]  # 盈利交易每次收益率
    win_prft = win_prft_rate.sum()  # 总盈利
    max_win_prft = win_prft_rate.max()  # 最大单次盈利
    loss_prft_rate = td_prft_rate[td_prft_rate < 0]  # 亏损交易每次收益率
    loss_prft = loss_prft_rate.sum()  # 总亏损
    max_loss_prft = loss_prft_rate.min()  # 最大单次亏损
    td_prft = win_prft + loss_prft  # 总交易利润
    td_count = len(td_prft_rate)  # 总交易次数
    win_count = len(win_prft_rate)  # 盈利交易次数
    loss_count = len(loss_prft_rate)  # 亏损交易次数
    win_ratio = win_count / td_count  # 胜率
    win_loss_ratio = 100 if loss_prft == 0 else win_prft / loss_prft  # 盈亏比
    avg_td_prft = td_prft / td_count  # 平均每笔利润
    avg_win_prft = 0 if win_count == 0 else win_prft / win_count  # 盈利平均每笔利润
    avg_loss_prft = 0 if loss_count == 0 else loss_prft / loss_count  # 亏损平均每笔利润

    # 持仓分析
    # 持仓标志：1==多，-1==空
    holding_flag = oc.replace(to_replace=0, method='ffill')
    data['holding_flag'] = holding_flag
    # 持仓时间序列（含平仓）
    pstn_srs = data.query('holding_flag==1 or oc==-1')
    # 持仓期间每期收益率
    pstn_prft_rate = np.exp(pstn_srs['logc'].diff(1)) - 1
    pstn_prft_rate[o_srs.index] = np.exp(o_srs['logp'] - o_srs['logc']) - 1  # 开仓当期收益率
    pstn_prft_rate[c_srs.index] = np.exp(
        pstn_prft_rate[c_srs.index] + c_srs['logc'] - c_srs['logp']) - 1  # 平仓当期收益率
    pstn_nv = (pstn_prft_rate + 1).cumprod()  # 持仓净值
    pstn_max_nv_srs = pstn_nv.rolling(window=len(pstn_prft_rate), min_periods=1).max()  # 最大净值序列
    pstn_max_drb_srs = (pstn_max_nv_srs - pstn_nv) / pstn_max_nv_srs * 100  # 最大回撤序列
    pstn_max_nv = pstn_max_nv_srs.max()
    pstn_max_drb = pstn_max_drb_srs.max()

    total_prd = data_len  # 总回测周期
    pstn_prd = len(pstn_srs)  # 持仓周期
    avg_pstn_prd = pstn_prd / td_count  # 每笔交易平均持仓周期
    per_pstn_prd = pd.Series(
        {c_srs.index[i]: len(data[o_srs.index[i]:c_srs.index[i]]) for i in range(len(o_srs))},
        name='ppp')
    per_win_prd = per_pstn_prd[td_prft_rate > 0]
    per_loss_prd = per_pstn_prd[td_prft_rate < 0]
    avg_win_prd = 0 if win_count == 0 else per_win_prd.sum() / win_count  # 盈利交易持仓平均周期
    avg_loss_prd = 0 if loss_count == 0 else per_loss_prd.sum() / loss_count  # 亏损交易持仓平均周期
    max_win_prd = per_win_prd.max()  # 盈利交易持仓最长周期
    max_loss_prd = per_loss_prd.max()  # 亏损交易持仓最长周期

    tr = TradeResultData()
    tr.oc_flag_srs = oc
    tr.td_srs = td_srs
    # tr.win_srs = win_srs
    # tr.loss_srs = loss_srs
    tr.td_prft_rate = td_prft_rate
    tr.td_nv = td_nv
    tr.td_max_nv_srs = td_max_nv_srs
    tr.td_max_nv = td_max_nv
    tr.td_max_drb_srs = td_max_drb_srs
    tr.td_max_drb = td_max_drb
    tr.win_prft_rate = win_prft_rate
    tr.win_prft = win_prft
    tr.max_win_prft = max_win_prft
    tr.loss_prft_rate = loss_prft_rate
    tr.loss_prft = loss_prft
    tr.max_loss_prft = max_loss_prft
    tr.td_prft = td_prft
    tr.td_count = td_count
    tr.win_count = win_count
    tr.loss_count = loss_count
    tr.win_ratio = win_ratio
    tr.win_loss_ratio = win_loss_ratio
    tr.avg_td_prft = avg_td_prft
    tr.avg_win_prft = avg_win_prft
    tr.avg_loss_prft = avg_loss_prft
    tr.pstn_nv = pstn_nv
    tr.pstn_max_nv_srs = pstn_max_nv_srs
    tr.pstn_max_nv = pstn_max_nv
    tr.pstn_max_drb_srs = pstn_max_drb_srs
    tr.pstn_max_drb = pstn_max_drb
    tr.total_prd = total_prd
    tr.pstn_prd = pstn_prd
    tr.avg_pstn_prd = avg_pstn_prd
    tr.per_pstn_prd = per_pstn_prd
    tr.per_win_prd = per_win_prd
    tr.per_loss_prd = per_loss_prd
    tr.avg_win_prd = avg_win_prd
    tr.avg_loss_prd = avg_loss_prd
    tr.max_win_prd = max_win_prd
    tr.max_loss_prd = max_loss_prd

    # 计算效率
    if not base_data:
        return tr

    return tr


# 测试
if __name__ == '__main__':
    btr0 = BoxiDict()
    btr0['td_list'] = [1, 2, 3]
    btr0.aa = 'ag'
    print(btr0.__dict__)
    btrd = TradeResultData
    print(clc_long_td_rslt.__doc__)

    for k in btrd.__dict__.keys():
        print('tr.', k, '=', k)
